Completed
Pull Request — master (#137)
by Sander
43s
created

background.js ➔ updateCredentialUrlDoorhanger   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 12
rs 9.4285
1
/* global API */
2
3
var background = (function () {
4
    var storage = new API.Storage();
5
    var _self = this;
6
    var _window = {};
7
8
9
    API.runtime.onConnect.addListener(function (port) {
10
11
        port.onMessage.addListener(function (msg) {
12
            if (msg === 'credential_amount') {
13
                port.postMessage('credential_amount:' + local_credentials.length);
14
            }
15
16
        });
17
18
    });
19
20
    var master_password = null;
21
22
    function getMasterPasswordSet() {
23
        return (master_password !== null);
24
    }
25
26
    _self.getMasterPasswordSet = getMasterPasswordSet;
27
28
    function setMasterPassword(opts) {
29
        master_password = opts.password;
30
        if (opts.hasOwnProperty('savePassword') && opts.savePassword === true) {
31
            // Save the password in plain text on user request.
32
            // No secure local storage is available :/
33
            storage.set('master_password', opts.password);
34
        } else {
35
            storage.set('master_password', null);
36
        }
37
38
        if (opts.password) {
39
            getSettings();
40
        } else {
41
            displayLogoutIcons();
42
        }
43
44
    }
45
46
    _self.setMasterPassword = setMasterPassword;
47
48
49
    var testMasterPasswordAgainst;
50
51
    function isMasterPasswordValid(password) {
52
        try {
53
            PAPI.decryptString(testMasterPasswordAgainst, password);
54
            return true;
55
        } catch (e) {
56
            return false;
57
        }
58
    }
59
60
    _self.isMasterPasswordValid = isMasterPasswordValid;
61
62
63
    var local_credentials = [];
64
    var local_vault = [];
65
    var encryptedFieldSettings = ['accounts'];
66
    _self.settings = {};
67
    _self.ticker = null;
68
    _self.running = false;
69
    function getSettings() {
70
71
        storage.get('settings').then(function (_settings) {
72
            if ((!_settings || Object.keys(_settings).length === 0 || !_settings.hasOwnProperty('accounts')) && !master_password) {
73
                return;
74
            }
75
76
            if (!master_password && _settings.hasOwnProperty('accounts') && _settings.accounts.length > 0) {
77
                _self.settings.isInstalled = 1;
78
                testMasterPasswordAgainst = _settings.accounts;
79
                return;
80
            }
81
82
            for (var i = 0; i < encryptedFieldSettings.length; i++) {
83
                var field = encryptedFieldSettings[i];
84
                _settings[field] = JSON.parse(PAPI.decryptString(_settings[field], master_password));
85
            }
86
87
            _self.settings = _settings;
88
89
            if (!_self.settings.hasOwnProperty('ignored_sites')) {
90
                _self.settings.ignored_sites = [];
91
            }
92
93
            if (!_self.settings.hasOwnProperty('no_results_found_tab')) {
94
                _self.settings.no_results_found_tab = 'list';
95
            }
96
97
            if (!_self.settings.hasOwnProperty('enablePasswordPicker')) {
98
                _self.settings.enablePasswordPicker = !_self.settings.disablePasswordPicker ;
99
            }
100
            
101
            if (!_self.settings.hasOwnProperty('enableAutoFill')) {
102
                _self.settings.enableAutoFill = !_self.settings.disableAutoFill;
103
            }
104
105
            getCredentials();
106
107
            if (_self.running) {
108
                clearInterval(_self.ticker);
109
            }
110
            _self.running = true;
111
            _self.ticker = setInterval(function () {
112
113
            }, _self.settings.refreshTime * 1000);
114
115
        });
116
    }
117
118
    _self.getSettings = getSettings;
119
120
    function getRuntimeSettings() {
121
        return _self.settings;
122
    }
123
124
    _self.getRuntimeSettings = getRuntimeSettings;
125
126
    function getSetting(name) {
127
        return _self.settings[name];
128
    }
129
130
    _self.getSetting = getSetting;
131
132
    function saveSettings(settings, cb) {
0 ignored issues
show
Unused Code introduced by
The parameter cb is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
133
        for (var i = 0; i < encryptedFieldSettings.length; i++) {
134
            var field = encryptedFieldSettings[i];
135
            settings[field] = PAPI.encryptString(JSON.stringify(settings[field]), master_password);
136
        }
137
138
        if (!settings.hasOwnProperty('ignored_sites')) {
139
            settings.ignored_sites = [];
140
        }
141
142
        if (!_self.settings.hasOwnProperty('password_picker_first_tab')) {
143
            _self.settings.disable_browser_autofill = 'list';
144
        }
145
146
        //window.settings contains the run-time settings
147
        _self.settings = settings;
148
149
150
        storage.set('settings', settings).then(function () {
151
            getSettings();
152
        });
153
154
    }
155
156
    _self.saveSettings = saveSettings;
157
158
159
    function getCredentials() {
160
        if (!master_password) {
161
            return;
162
        }
163
        //console.log('Loading vault with the following settings: ', settings);
164
        var tmpList = [];
165
166
        for (var i = 0; i < _self.settings.accounts.length; i++) {
167
            var account = _self.settings.accounts[i];
168
            /* jshint ignore:start */
169
            (function (inner_account) {
170
                PAPI.getVault(inner_account, function (vault) {
171
                    if (vault.hasOwnProperty('error')) {
172
                        return;
173
                    }
174
                    var _credentials = vault.credentials;
175
                    for (var i = 0; i < _credentials.length; i++) {
176
                        var key = inner_account.vault_password;
177
                        var credential = _credentials[i];
178
                        if (credential.hidden === 1) {
179
                            continue;
180
                        }
181
                        var usedKey = key;
182
                        //Shared credentials are not implemented yet
183
                        if (credential.hasOwnProperty('shared_key') && credential.shared_key) {
184
                            usedKey = PAPI.decryptString(credential.shared_key, key);
185
186
                        }
187
                        credential = PAPI.decryptCredential(credential, usedKey);
188
                        credential.account = inner_account;
189
                        if (credential.delete_time === 0) {
190
                            tmpList.push(credential);
191
                        }
192
193
                    }
194
                    delete vault.credentials;
195
                    local_vault = vault;
196
                    local_credentials = tmpList;
197
198
                    getSharedCredentials(inner_account);
199
200
201
                });
202
            }(account));
203
            /* jshint ignore:end */
204
        }
205
    }
206
207
    _self.getCredentials = getCredentials;
208
209
    function getSharedCredentials(account) {
210
        PAPI.getCredendialsSharedWithUs(account, account.vault.guid, function (credentials) {
211
            for (var i = 0; i < credentials.length; i++) {
212
                var _shared_credential = credentials[i];
213
                var _shared_credential_data;
214
                var sharedKey = PAPI.decryptString(_shared_credential.shared_key, account.vault_password);
215
                try {
216
                    _shared_credential_data = PAPI.decryptSharedCredential(_shared_credential.credential_data, sharedKey);
217
                } catch (e) {
0 ignored issues
show
Coding Style Comprehensibility Best Practice introduced by
Empty catch clauses should be used with caution; consider adding a comment why this is needed.
Loading history...
218
219
                }
220
                if (_shared_credential_data) {
221
                    delete _shared_credential.credential_data;
222
                    _shared_credential_data.acl = _shared_credential;
223
                    _shared_credential_data.acl.permissions = new SharingACL(_shared_credential_data.acl.permissions);
0 ignored issues
show
Bug introduced by
The variable SharingACL seems to be never declared. If this is a global, consider adding a /** global: SharingACL */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
224
                    _shared_credential_data.tags_raw = _shared_credential_data.tags;
225
                    _shared_credential_data.account = account;
226
                    local_credentials.push(_shared_credential_data);
227
                }
228
            }
229
            updateTabsIcon();
230
        });
231
    }
232
233
    function getCredentialsByUrl(_url, sender) {
0 ignored issues
show
Unused Code introduced by
The parameter sender is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
234
        if (!master_password) {
235
            return [];
236
        }
237
        if (!_url || _url === '') {
238
            return [];
239
        }
240
        var url = processURL(_url, _self.settings.ignoreProtocol, _self.settings.ignoreSubdomain, _self.settings.ignorePath, _self.settings.ignorePort);
241
        var found_list = [];
242
        for (var i = 0; i < local_credentials.length; i++) {
243
            var credential_url = local_credentials[i].url;
244
            if (!/^(ht)tps?:\/\//i.test(credential_url) && credential_url !== '' && _url) {
245
                try {
246
                    var protocol = _url.split('://').shift();
247
                    credential_url = protocol + "://" + credential_url;
248
                } catch (e){
249
                    //ignore
250
                }
251
            }
252
            credential_url = processURL(credential_url, _self.settings.ignoreProtocol, _self.settings.ignoreSubdomain, _self.settings.ignorePath, _self.settings.ignorePort);
253
            if (credential_url) {
254
                if (credential_url.indexOf(url) !== -1) {
255
                    found_list.push(local_credentials[i]);
256
                }
257
            }
258
259
        }
260
        return found_list;
261
    }
262
263
    _self.getCredentialsByUrl = getCredentialsByUrl;
264
265
266
    function saveCredential(credential) {
267
        //@TODO save shared password
268
        if (!credential.credential_id) {
269
            PAPI.createCredential(credential.account, credential, credential.account.vault_password, function (createdCredential) {
270
                local_credentials.push(createdCredential);
271
            });
272
        } else {
273
            var credential_index;
274
            for (var i = 0; i < local_credentials.length; i++) {
275
                if (local_credentials[i].guid === credential.guid) {
276
                    credential_index = i;
277
                    break;
278
                }
279
            }
280
281
            if (credential.hasOwnProperty('acl')) {
282
                var permissons = new SharingACL(credential.acl.permissions.permission);
0 ignored issues
show
Bug introduced by
The variable SharingACL seems to be never declared. If this is a global, consider adding a /** global: SharingACL */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
283
                if (!permissons.hasPermission(0x02)) {
284
                    return;
285
                }
286
            }
287
288
            PAPI.updateCredential(credential.account, credential, credential.account.vault_password, function (updatedCredential) {
289
                if (credential_index) {
290
                    local_credentials[credential_index] = updatedCredential;
291
                }
292
            });
293
        }
294
    }
295
296
    _self.saveCredential = saveCredential;
297
298
    function getCredentialByGuid(guid) {
299
        for (var i = 0; i < local_credentials.length; i++) {
300
            var credential = local_credentials[i];
301
            if (credential.guid === guid) {
302
                return credential;
303
            }
304
        }
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
305
    }
306
307
    _self.getCredentialByGuid = getCredentialByGuid;
308
309
    function getCredentialForHTTPAuth(req) {
310
        return getCredentialsByUrl(req.url)[0];
311
    }
312
313
    _window.getCredentialForHTTPAuth = getCredentialForHTTPAuth;
314
315
    var mined_data = [];
316
317
    function minedForm(data, sender) {
318
        var url = sender.url;
319
        var existingLogins = getCredentialsByUrl(sender.url);
320
        var title = API.i18n.getMessage('detected_new_login') + ':';
321
        var minedMatchingID = null;
322
        for (var j = 0; j < existingLogins.length; j++) {
323
            var login = existingLogins[j];
324
            if (login.username === data.username) {
325
                if (login.password !== data.password) {
326
                    minedMatchingID = login.guid;
327
                    title = API.i18n.getMessage('detected_changed_login') + ':';
328
                }
329
                else {
330
                    //console.log('No changes detected');
331
                    delete mined_data[sender.tab.id];
332
                    return;
333
                }
334
            }
335
        }
336
        mined_data[sender.tab.id] = {
337
            title: title,
338
            url: url,
339
            username: data.username,
340
            password: data.password,
341
            label: sender.title,
342
            guid: minedMatchingID
343
        };
344
345
        //console.log('Done mining, ', mined_data, sender.tab.id);
346
    }
347
348
    _self.minedForm = minedForm;
349
350
    function getMinedData(args, sender) {
351
        //console.log('Fecthing  mined data for tab id', sender.tab.id)
352
        var senderUrl = sender.tab.url;
353
        var site = processURL(senderUrl, _self.settings.ignoreProtocol, _self.settings.ignoreSubdomain, _self.settings.ignorePath, _self.settings.ignorePort);
354
        if (!_self.settings) {
355
            return null;
356
        }
357
        if (!_self.settings.hasOwnProperty('ignored_sites')) {
358
            return mined_data[sender.tab.id];
359
        }
360
        var matches = _self.settings.ignored_sites.filter(function (item) {
361
            return typeof item === 'string' && site.indexOf(item) > -1;
362
        });
363
364
        if (matches.length !== 0) {
365
            return null;
366
        }
367
        return mined_data[sender.tab.id];
368
    }
369
370
    _self.getMinedData = getMinedData;
371
372
    function clearMined(args, sender) {
373
        delete mined_data[sender.tab.id];
374
    }
375
376
    _self.clearMined = clearMined;
377
378
    function saveMinedCallback(args) {
379
        createIconForTab(args.sender.tab);
380
        API.tabs.query({active: true, currentWindow: true}).then(function (tabs) {
381
            API.tabs.sendMessage(args.sender.tab.id, {method: "minedLoginSaved", args: args}).then(function (response) {
0 ignored issues
show
Unused Code introduced by
The parameter response is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
382
            });
383
        });
384
    }
385
386
    function ignoreSite(_url) {
387
        if (!_self.settings.hasOwnProperty('ignored_sites')) {
388
            _self.settings.ignored_sites = [];
389
        }
390
        var site = processURL(_url, _self.settings.ignoreProtocol, _self.settings.ignoreSubdomain, _self.settings.ignorePath, _self.settings.ignorePort);
391
        if (_self.settings.ignored_sites.indexOf(site) === -1) {
392
            _self.settings.ignored_sites.push(site);
393
            saveSettings(_self.settings);
394
        }
395
    }
396
397
    _self.ignoreSite = ignoreSite;
398
399
    function passToParent(args, sender) {
400
        API.tabs.sendMessage(sender.tab.id, {method: args.injectMethod, args: args.args}).then(function (response) {
0 ignored issues
show
Unused Code introduced by
The parameter response is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
401
        });
402
    }
403
404
    _self.passToParent = passToParent;
405
406
    function getActiveTab(opt) {
407
        API.tabs.query({active: true, currentWindow: true}).then(function (tabs) {
408
            var tab = tabs[0];
409
            API.tabs.sendMessage(tab.id, {method: opt.returnFn, args: tab}).then(function (response) {
0 ignored issues
show
Unused Code introduced by
The parameter response is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
410
            });
411
        });
412
    }
413
414
    _self.getActiveTab = getActiveTab;
415
416
    function updateCredentialUrlDoorhanger(login) {
417
        API.tabs.query({active: true, currentWindow: true}).then(function (tabs) {
418
            var tab = tabs[0];
419
            var data = login;
420
            data.url = tab.url;
421
            data.title = API.i18n.getMessage('detected_changed_url') + ':';
422
            API.tabs.sendMessage(tab.id, {
423
                method: 'showUrlUpdateDoorhanger',
424
                args: {data: data}
425
            });
426
        });
427
    }
428
429
    _self.updateCredentialUrlDoorhanger = updateCredentialUrlDoorhanger;
430
431
    function updateCredentialUrl(data, sender) {
432
        mined_data[sender.tab.id] = data;
433
        saveMined({}, sender);
434
435
    }
436
437
    _self.updateCredentialUrl = updateCredentialUrl;
438
439
    function saveMined(args, sender) {
440
        var data = mined_data[sender.tab.id];
441
        var credential = {},
442
            credential_index;
443
444
        if (data.guid === null) {
445
            credential = PAPI.newCredential();
446
        } else {
447
            for (var i = 0; i < local_credentials.length; i++) {
448
                if (local_credentials[i].guid === data.guid) {
449
                    credential = local_credentials[i];
450
                    credential_index = i;
451
                    break;
452
                }
453
            }
454
        }
455
        if (!credential.hasOwnProperty('account')) {
456
            credential.account = args.account;
457
        }
458
        credential.username = data.username;
459
        credential.password = data.password;
460
        credential.url = sender.tab.url;
461
        if (credential.guid !== null) {
462
            PAPI.updateCredential(credential.account, credential, credential.account.vault_password, function (updatedCredential) {
463
                updatedCredential.account = credential.account;
464
                if (credential_index) {
465
                    local_credentials[credential_index] = updatedCredential;
466
                }
467
                saveMinedCallback({credential: credential, updated: true, sender: sender});
468
                delete mined_data[sender.tab.id];
469
            });
470
        } else {
471
            credential.label = sender.tab.title;
472
            credential.vault_id =  credential.account.vault.vault_id;
473
            PAPI.createCredential(credential.account, credential, credential.account.vault_password, function (createdCredential) {
474
                createdCredential.account = credential.account;
475
                saveMinedCallback({credential: credential, updated: false, sender: sender});
476
                local_credentials.push(createdCredential);
477
                delete mined_data[sender.tab.id];
478
            });
479
        }
480
    }
481
482
    _self.saveMined = saveMined;
483
484
    function searchCredential(searchText) {
485
        searchText = searchText.toLowerCase();
486
        var searchFields = ['label', 'username', 'email', 'url', 'description'];
487
        var results = [];
488
        for (var i = 0; i < local_credentials.length; i++) {
489
            var credential = local_credentials[i];
490
            for (var f = 0; f < searchFields.length; f++) {
491
                var field = searchFields[f];
492
                if (!credential[field]) {
493
                    continue;
494
                }
495
496
                var field_value = credential[field].toLowerCase();
497
                if (field_value.indexOf(searchText) !== -1) {
498
                    results.push(credential);
499
                    break;
500
                }
501
            }
502
        }
503
        return results;
504
    }
505
506
    _self.searchCredential = searchCredential;
507
508
509
    function injectCreateCredential(args, sender) {
510
        var account = getRuntimeSettings().accounts[args.vaultIndex];
511
        var credential = PAPI.newCredential();
512
        credential.label = args.label;
513
        credential.username = args.username;
514
        credential.password = args.password;
515
        credential.vault_id = local_vault.vault_id;
516
        credential.url = sender.tab.url;
517
        PAPI.createCredential(account, credential, account.vault_password, function (createdCredential) {
518
            saveMinedCallback({credential: credential, updated: false, sender: sender, selfAdded: true});
519
            local_credentials.push(createdCredential);
520
521
        });
522
    }
523
524
    self.injectCreateCredential = injectCreateCredential;
0 ignored issues
show
Bug introduced by
The variable self seems to be never declared. If this is a global, consider adding a /** global: self */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
525
526
    function isVaultKeySet() {
527
        return (_self.settings.vault_password !== null);
528
    }
529
530
    _self.isVaultKeySet = isVaultKeySet;
531
532
    function isAutoFillEnabled() {
533
        if (!_self.settings.hasOwnProperty('enableAutoFill')) {
534
            return true;
535
        }
536
        return _self.settings.enableAutoFill;
537
    }
538
539
    _self.isAutoFillEnabled = isAutoFillEnabled;
540
541
    function isAutoSubmitEnabled() {
542
        if (!_self.settings.hasOwnProperty('enableAutoSubmit')) {
543
            return false;
544
        }
545
        return _self.settings.enableAutoSubmit;
546
    }
547
548
    _self.isAutoSubmitEnabled = isAutoSubmitEnabled;
549
550
    var doorhangerData = null;
551
552
    function setDoorhangerData(data) {
553
        doorhangerData = data;
554
    }
555
556
    _self.setDoorhangerData = setDoorhangerData;
557
558
    function getDoorhangerData() {
559
        return doorhangerData;
560
    }
561
562
    _self.getDoorhangerData = getDoorhangerData;
563
564
    function closeSetupTab() {
565
        API.tabs.query({url: 'chrome-extension://' + API.runtime.id + '/html/browser_action/browser_action.html'}).then(function (tabs) {
566
            if (tabs) {
567
                API.tabs.remove(tabs[0].id);
568
            }
569
        });
570
    }
571
572
    _self.closeSetupTab = closeSetupTab;
573
574
    API.runtime.onMessage.addListener(function (msg, sender, sendResponse) {
575
576
        if (!msg || !msg.hasOwnProperty('method')) {
577
            return;
578
        }
579
        var result = false;
580
        if (_self[msg.method]) {
581
            result = _self[msg.method](msg.args, sender);
582
        } else {
583
            console.warn('[NOT FOUND] Method call', msg.method, 'args: ', msg.args);
584
        }
585
586
        sendResponse(result);
587
    });
588
589
    var defaultColor = '#0082c9';
590
591
    function createIconForTab(tab) {
592
        if (!master_password) {
593
            return;
594
        }
595
        var tabUrl = tab.url;
596
        var logins = getCredentialsByUrl(tabUrl);
597
        if (tab.active) {
598
            window.contextMenu.setContextItems(logins);
599
        }
600
        var credentialAmount = logins.length;
601
        API.browserAction.setBadgeText({
602
            text: credentialAmount.toString(),
603
            tabId: tab.id
604
        });
605
        API.browserAction.setBadgeBackgroundColor({
606
            color: defaultColor,
607
            tabId: tab.id
608
        });
609
610
        var plural = (credentialAmount === 1) ? API.i18n.getMessage('credential') : API.i18n.getMessage('credentials');
611
        API.browserAction.setTitle({
612
            title: API.i18n.getMessage('browser_action_title_login', [credentialAmount.toString(), plural.toString()]),
613
            tabId: tab.id
614
        });
615
    }
616
617
    function displayLogoutIcons() {
618
        if (_self.settings) {
619
            API.tabs.query({}).then(function (tabs) {
620
                for (var t = 0; t < tabs.length; t++) {
621
                    var tab = tabs[t];
622
                    API.browserAction.setBadgeText({
623
                        text: '🔑',
624
                        tabId: tab.id
625
                    });
626
                    API.browserAction.setBadgeBackgroundColor({
627
                        color: '#ff0000',
628
                        tabId: tab.id
629
                    });
630
                    API.browserAction.setTitle({
631
                        title: API.i18n.getMessage('browser_action_title_locked'),
632
                        tabId: tab.id
633
                    });
634
                }
635
            });
636
        }
637
    }
638
639
    function updateTabsIcon() {
640
        API.tabs.query({}).then(function (tabs) {
641
            for (var t = 0; t < tabs.length; t++) {
642
                var tab = tabs[t];
643
                createIconForTab(tab);
644
            }
645
        });
646
    }
647
648
649
    API.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
650
        if (master_password) {
651
            createIconForTab(tab);
652
        } else {
653
            displayLogoutIcons();
654
        }
655
    });
656
657
    API.tabs.onActivated.addListener(function () {
658
        API.tabs.query({active: true, currentWindow: true}).then(function (tabs) {
659
            if (master_password) {
660
                createIconForTab(tabs[0]);
661
            } else {
662
                displayLogoutIcons();
663
            }
664
        });
665
    });
666
667
    displayLogoutIcons();
668
669
670
    storage.get('master_password').then(function (password) {
671
        if (password) {
672
            master_password = password;
673
            API.api.browserAction.setBadgeBackgroundColor({
674
                color: defaultColor
675
            });
676
        }
677
        getSettings();
678
    }).error(function (error) {
679
        if (error === "Data not found") {
680
            getSettings();
681
        }
682
    });
683
    return _window;
684
}());
685
686